package com.example.butterknifecoredemo.ioc;

import android.util.Log;
import android.view.View;

import com.example.lib_annotations.BindView;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 注解注入工具类
 */
public class InjectUtils {

    public static void inject(Object context) {
        injectLayout(context);
        injectView(context);
        try {
            injectClick(context);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    /**
     * 布局注入
     */
    private static void injectLayout(Object context) {
        // 获取注解的 LayoutId
        int layoutId = 0;
        Class<?> clazz = context.getClass();
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {
            layoutId = contentView.value();
        }
        // 反射执行setContentView()
        try {
            Method method = context.getClass().getMethod("setContentView", int.class);
            method.invoke(context, layoutId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 控件注入
     */
    private static void injectView(Object context) {
        Class<?> clazz = context.getClass();

        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            BindView bindView = field.getAnnotation(BindView.class);
            if (bindView != null) {
                int viewId = bindView.value();
                // 反射执行findViewById()
                try {
                    Method method = context.getClass().getMethod("findViewById", int.class);
                    View view = (View) method.invoke(context, viewId);
                    field.setAccessible(true);
                    // context.filed = view。 即，对注解下的变量赋值
                    field.set(context, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void injectClick(Object context) throws NoSuchMethodException {
        Class<?> clazz = context.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            // 遍历方法上所有的注解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                Class<? extends Annotation> annotationType = annotation.annotationType();
                EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                if (eventBase != null) {
                    String listenerSetter = eventBase.listenerSetter();
                    Class<?> listenerType = eventBase.listenerType();
                    String callBackMethod = eventBase.callBackMethod();
                    Log.e("111", "callBackMethod = " + callBackMethod);
                    Method valueMethod = null;

                    // 反射得到ID，再根据Id得到对应的View

                    valueMethod = annotationType.getDeclaredMethod("value");
                    try {
                        int[] viewId = (int[]) valueMethod.invoke(annotation);
                        for (int id : viewId) {
                            Method findViewById = clazz.getMethod("findViewById", int.class);
                            View view = (View) findViewById.invoke(context, id);
                            // 对View执行监听(使用代理，代理View.OnClickListener接口)
                            Log.e("111", "传入的Method = " + method);
                            ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(context, method);
                            // java.lang.BootClassLoader@9b09f95 、 View.OnClickListener.class、
                            Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);
                            /**
                             * proxyInstance 代理的是View.OnclickListener的方法，当onClick的事件被调用的时候，使用Activity里面声明的方法进行实际的调用。即用Activity声明的
                             * 方法代理View.OnclickListener的onClick方法
                             *
                             * 再不明白的：推荐看视频学习一遍： https://www.bilibili.com/video/BV1HZ4y1p7F1?from=search&seid=17721653553126650704
                             */
                            Method onclickMethod = view.getClass().getMethod(listenerSetter, listenerType);
                            Log.e("111", "onclickMethod = " + onclickMethod.toString());
                            onclickMethod.invoke(view, proxyInstance); // 设置OnClickListener监听回调
                        }
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

}
